﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Xml.Linq;

namespace DicomLibrary
{
    internal class DReader
    {
        internal DTransferSyntax TransferSyntax { get; set; }
        internal BinaryReader Reader { get; set; }

        internal DReader(BinaryReader reader) : this(reader, DTransferSyntax.ExplicitLittleEndian) { }
        internal DReader(BinaryReader reader, DTransferSyntax transferSyntax)
        {
            this.Reader = reader;
            this.TransferSyntax = transferSyntax;
        }

        internal List<DDataElement> ReadMetaInformation()
        {
            var metaInformation = new List<DDataElement>();
            DDataElement groupLength = ReadNextDataElement();
            metaInformation.Add(groupLength);
            long basePosition = Reader.BaseStream.Position;
            while (basePosition + (int)groupLength.Value > Reader.BaseStream.Position)
            {
                metaInformation.Add(ReadNextDataElement());
            }

            return metaInformation;
        }

        internal DDataElement ReadNextDataElement()
        {
            DDataElement dataElement = new DDataElement();
            dataElement.Tag = ReadNextTag();
            if (TransferSyntax.IsExplicit())
            {
                dataElement.ValueRepresentation = ReadNextValueRepresentation();
            }
            else
            {

                //This might need some optimalisation
                XDocument doc = XDocument.Load(@"DataDictionary.xml");
                var q = from c in doc.Descendants("Tag")
                        where String.Equals((string)c.Element("GroupNumber"), dataElement.Tag.GroupNumber.ToString("x4"), StringComparison.OrdinalIgnoreCase) &&
                                String.Equals((string)c.Element("ElementNumber"), dataElement.Tag.ElementNumber.ToString("x4"), StringComparison.OrdinalIgnoreCase)
                        select (string)c.Element("ValueRepresentation");
                 string VR = q.First();

                 short vrbe = BitConverter.ToInt16(System.Text.Encoding.ASCII.GetBytes(VR), 0); //Convert string to byte for easy enum;
                 short vr = (short)(((vrbe & 0xff) << 8) | ((vrbe >> 8) & 0xff)); //It's bit endian-> lets convert it to little endian
                 dataElement.ValueRepresentation = (DValueRepresentation)vr; //Cast to enum
            }
            dataElement.ValueLength = ReadNextValueLength(dataElement.ValueRepresentation);
            dataElement.Value = ReadNextValue(dataElement.ValueRepresentation, dataElement.ValueLength);
            return dataElement;
        }

        internal DTag ReadNextTag()
        {
            return new DTag(
                Reader.ReadInt16(),
                Reader.ReadInt16()
                );
        }

        internal int ReadNextValueLength(DValueRepresentation valueRepresentation)
        {
            switch (valueRepresentation)
            {
                case DValueRepresentation.OtherByte:
                case DValueRepresentation.OtherWord:
                    if (TransferSyntax.IsExplicit())
                    {
                        Reader.ReadBytes(2); //Skip 2
                    }
                    return Reader.ReadInt32();
                case DValueRepresentation.SequenceOfItems:
                    throw new NotImplementedException();
                default:
                    if (TransferSyntax.IsExplicit())
                    {
                        return Reader.ReadInt16();
                    }
                    else
                    {
                        return Reader.ReadInt32();
                    }
            }

        }

        internal DValueRepresentation ReadNextValueRepresentation()
        {
            short vrbe = BitConverter.ToInt16(Reader.ReadBytes(2),0);
            short vr = (short)(((vrbe & 0xff) << 8) | ((vrbe >> 8) & 0xff));
            if (Enum.IsDefined(typeof(DValueRepresentation), vr))
            {
                return (DValueRepresentation)vr;
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        internal object ReadNextValue(DValueRepresentation valueRepresentation, int valueLength)
        {
            switch (valueRepresentation)
            {
                case DValueRepresentation.UnsignedShort:
                    if (valueLength == 2)
                    {
                        return Reader.ReadUInt16();
                    }
                    else
                    {
                        ushort[] shortValues = new ushort[valueLength / 2];
                        for (int i = 0; i < valueLength; i += 2)
                        {
                            shortValues[i / 2] = Reader.ReadUInt16();
                        }
                        return shortValues;
                    }
                case DValueRepresentation.AttributeTag:
                case DValueRepresentation.UnsignedLong:
                    return Reader.ReadInt32();
                case DValueRepresentation.SequenceOfItems:
                    throw new NotImplementedException();
                case DValueRepresentation.OtherByte:
                    return Reader.ReadBytes(valueLength);
                case DValueRepresentation.OtherWord:
                    ushort[] wordValues = new ushort[valueLength / 2];
                    for (int i = 0; i < valueLength; i += 2)
                    {
                        wordValues[i / 2] = Reader.ReadUInt16();
                    }
                    return wordValues;
                case DValueRepresentation.UniqueIdentifier:
                    return ASCIIEncoding.ASCII.GetString(Reader.ReadBytes(valueLength)).TrimEnd('\0');
                case DValueRepresentation.SignedShort:
                case DValueRepresentation.ApplicationEntity:
                case DValueRepresentation.CodeString:
                case DValueRepresentation.Date:
                case DValueRepresentation.Time:
                case DValueRepresentation.LongString:
                case DValueRepresentation.PersonName:
                case DValueRepresentation.AgeString:
                case DValueRepresentation.DecimalString:
                case DValueRepresentation.IntegerString:
                case DValueRepresentation.LongText:
                case DValueRepresentation.DateTime:
                case DValueRepresentation.ShortString:
                case DValueRepresentation.ShortText:
                    return ASCIIEncoding.ASCII.GetString(Reader.ReadBytes(valueLength));
                default:
                    throw new NotImplementedException();
            }
        }

 
    }
}
